
#include <ddk.h>
#include <stdlib.h>

#include "UffsNandFlashManager.h"

#include "uffs/uffs_config.h"
#include "uffs/uffs_fs.h"
#include "uffs/uffs_utils.h"
#include "uffs\uffs_os.h"

extern "C" void femu_init_uffs_device(uffs_Device *dev);

UffsNandFlashManager::UffsNandFlashManager()
{
    int i = 0;
    int nMountTableSize = sizeof(uffs_mountTable);
    int nDeviceStSize = sizeof(uffs_Device);

    m_nIdx = 0;
    for (i = 0; i < MAX_MOUNT_TABLES; i++) {
        memset(&m_conf_mounts[i], 0, nMountTableSize);
        memset(&m_conf_devices[i], 0, nDeviceStSize);
        memset(m_mount_point_name[i], 0, MAX_MOUNT_POINT_NAME);
    }

    m_conf_mounts[0].dev = &(m_conf_devices[0]);
	m_conf_mounts[0].mountPoint = "/";
	m_conf_mounts[0].startBlock = 0;
	m_conf_mounts[0].endBlock = -1;
}

ECode UffsNandFlashManager::constructor()
{
/**
*   init local device:
*
*   the first partition is fix: / , with startBlock = 0, endBlock = -1(indicate the last block)
*
*   then you can mount other partition with forms: /partition_name/ as its partition name.
*   by calling MountPartition(), the other arguments can identify the block zone.
*/



/**
*   init a nand flash with default value:
*
*   Page = 512 bytes:
*         PagesPerBlock = 32
*         PageDataSize = 512Byte
*         PageSpareDataSize = 16
*         TotalBlocks = 128
*
*   Page = 2K bytes:
*         PagesPerBlock = 64
*         PageDataSize = 2K bytes
*         PageSpareDataSize = 64
*         TotalBlocks = 1000
*/
//    m_nPagesPerBlock = 64;
//    m_nPageDataSize = 2048;
//    m_nPageSpareSize = 64;
//    m_nTotalBlocks = 1000;


    return NOERROR;
}

UffsNandFlashManager::~UffsNandFlashManager()
{
}

#ifdef USE_NATIVE_MEMORY_ALLOCATOR

/**
 * Allocate 100K memory for native memory allocator.
 * In your final product, either disable the native
 * memory allocator or use the rest of system heap
 * instead of allocate this big array here :)
 */
u32 g_mem_pool[(1024 * 100) / sizeof(u32)];

#endif

ECode UffsNandFlashManager::Initialize()
{
	static int bIsFileSystemInited = 0;
	URET ret = 0;
	struct uffs_mountTableSt *mtbl = &(m_conf_mounts[0]);

    if(bIsFileSystemInited) {
	    kprintf("bIsFileSystemInited true\n");
	    return -4;
	}

	bIsFileSystemInited = 1;
    uffs_InitCS();

	if (m_nIdx == 0) {
	    femu_init_uffs_device(m_conf_mounts[0].dev);
	    m_nIdx++;
	}

#ifdef USE_NATIVE_MEMORY_ALLOCATOR
	uffs_InitHeapMemory(g_mem_pool, sizeof(g_mem_pool));
#endif

	while(mtbl->dev) {
		uffs_RegisterMountTable(mtbl);
		mtbl++;
	}

	ret = uffs_initMountTable();
	if (ret == U_SUCC) {
	    return 0;
	}

	return -1;
}

ECode UffsNandFlashManager::Uninit()
{
    uffs_releaseMountTable();

    uffs_UninitCS();

    return NOERROR;
}

ECode UffsNandFlashManager::Mount(
    /* [in] */ Int32 nStartBlock,
    /* [in] */ Int32 nEndBlock,
    /* [in] */ const char *mountPoint)
{
    struct uffs_mountTableSt *mtbl = &(m_conf_mounts[m_nIdx]);

    mtbl->mountPoint = &(m_mount_point_name[m_nIdx][0]);

    strcpy((char*)(mtbl->mountPoint), mountPoint);//, nLen);

    mtbl->startBlock = nStartBlock;
    mtbl->endBlock = nEndBlock;

    mtbl->dev = &(m_conf_devices[m_nIdx]);
    femu_init_uffs_device(mtbl->dev);

    m_nIdx++;

    return NOERROR;
}

ECode UffsNandFlashManager::PrintMountStat()
{
    struct uffs_mountTableSt *mtbl;

    for (int i = 0; i < m_nIdx; i++) {
        mtbl = &(m_conf_mounts[i]);
        kprintf("%s\n", mtbl->mountPoint);
    }

    return NOERROR;
}

ECode UffsNandFlashManager::Format(
    /* [in] */ WString devName)
{
    URET ret;
    uffs_Device *dev;
    char mount[MAX_MOUNT_POINT_NAME] = {0};
	kprintf("Formating %s ... ", mount);

    WcsToMbs(mount, devName, MAX_MOUNT_POINT_NAME);
	dev = uffs_GetDevice(mount);
	if (dev == NULL) {
		kprintf("Can't get device from mount point.\n");
		return -1;
	}
	else {
		ret = uffs_FormatDevice(dev);
		if (ret != U_SUCC) {
			kprintf("Format fail.\n");
		}
		else {
			kprintf("Format succ.\n");
		}
	}

	uffs_PutDevice(dev);

	return ret;
}

ECode UffsNandFlashManager::GetCurrentDir(
    /* [out] */ WStringBuf *pwszCurDir)
{
    const char *path = uffs_GetCurrentPath(NULL);
    wchar_t wszPath[MAX_PATH_LENGTH] = {0};

    if (pwszCurDir == NULL) {
        return E_INVALID_ARGUMENT;
    }

    MbsToWcs(wszPath, path, MAX_PATH_LENGTH);

    pwszCurDir->Copy(wszPath);
    return NOERROR;
}

ECode UffsNandFlashManager::SetCurrentDir(
    /* [in] */ WString wszCurDir)
{
    char szName[MAX_PATH_LENGTH] = {0};

    if (wszCurDir.IsNullOrEmpty()) {
        return E_INVALID_ARGUMENT;
    }

    WcsToMbs(szName, wszCurDir, MAX_PATH_LENGTH);

    if (uffs_SetCurrentPath(szName) == U_SUCC) {
		kprintf("current dir changed to: %s\n", uffs_GetCurrentPath(NULL));
	}
	else {
		kprintf("Can't change dir to: %s\n", szName);
	}

	return NOERROR;
}

